package com.example.mq.mqserver.core;

import com.example.mq.common.MqException;

// 实现交换机机得转发规则
// 验证binding是否合法
public class Router {
    // 验证binding是否合法
    public boolean checkBindingKey(String bindingKey){
        if(bindingKey.isEmpty()) {
            // 合法，在使用direct / fanout交换机时，bindingKey用不上可设置为空
            return true;
        }
        // 检测字符串不能存在非法字符
        for (int i = 0; i < bindingKey.length(); i++) {
            char ch = bindingKey.charAt(i);
            if(ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9' || ch == '_' || ch == '#' || ch == '.' || ch == '*') {
                continue;
            }
            return false;
        }
        // 检测 * 或者 # 是否是独立的部分
        String[] words = bindingKey.split("\\.");
        for (String word : words) {
            if(word.length() > 1 && (word.contains("*") || word.contains("#"))) {
                return false;
            }
        }
        // 约定通配符之间的相邻关系
        for (int i = 0; i < words.length - 1; i++) {
            // 连续两个#
            if(words[i].equals("#") && words[i+1].equals("#")) {
                return false;
            }
            // # *
            if(words[i].equals("#") && words[i+1].equals("*")) {
                return false;
            }
            // * #
            if(words[i].equals("*") && words[i+1].equals("#")) {
                return false;
            }
        }
        return true;
    }

    public boolean checkRoutingKey(String routingKey){
        if(routingKey.isEmpty()) {
            // 合法情况，例如fanout交换机，routingKey用不上可设置为空
            return true;
        }
        // routingKey构造规则
        // 1.数字，字母，下划线
        // 2. . 分割字符串
        for (int i = 0; i < routingKey.length(); i++) {
            char ch = routingKey.charAt(i);
            if(ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z' || ch == '_' || ch == '.' || ch >= '0' && ch <= '9') {
                continue;
            }
            // 非法字符
            return false;
        }
        // 没有非法情况
        return true;
    }

    // 判断消息是否能够转发
    public boolean route(ExchangeType exchangeType,Binding binding,Message message) throws MqException {
        // 根据exchangeType判断
        if(exchangeType == ExchangeType.FANOUT) {
            return true;
        }else if(exchangeType == ExchangeType.TOPIC) {
            // 主题交换机
            return routeTopic(binding,message);
        }else {
            throw new MqException("不支持的交换机类型,exchangeType=" + exchangeType);
        }
    }

    private boolean routeTopic(Binding binding, Message message) {
        // 切分
        String[] bindingTokens = binding.getBindingKey().split("\\.");
        String[] routingTokens = message.getRoutingKey().split("\\.");

        int bindingIndex = 0;
        int routingIndex = 0;
        while(bindingIndex < bindingTokens.length && routingIndex < routingTokens.length) {
            if(bindingTokens[bindingIndex].equals("*")) {
                bindingIndex++;
                routingIndex++;
                continue;
            } else if (bindingTokens[bindingIndex].equals("#")) {
                bindingIndex++;
                if(bindingIndex == bindingTokens.length) {
                    // # 后面没东西了
                    return true;
                }
                routingIndex = findNextMatch(routingTokens, routingIndex, bindingTokens[bindingIndex]);
                if(routingIndex == -1) {
                    // 没找到
                    return false;
                }
                // 找到了
                bindingIndex++;
                routingIndex++;
            }
            else {
                if(!bindingTokens[bindingIndex].equals(routingTokens[routingIndex])) {
                    return false;
                }
                bindingIndex++;
                routingIndex++;
            }
        }
        // 判断双方同时到达末尾
        if(bindingIndex == bindingTokens.length && routingIndex == routingTokens.length) {
            return true;
        }
        return false;
    }

    private int findNextMatch(String[] routingTokens, int routingIndex, String bindingToken) {
        for (int i = routingIndex; i < routingTokens.length; i++) {
            if (routingTokens[i].equals(bindingToken)) {
                return i;
            }
        }
        return -1;
    }

}
